﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
using System.Text.RegularExpressions;
using Util.IO;

namespace Util.Xml
{
    /*
     * Xml序列化
     */
    public interface XmlSerializable
    {
        /*
         * Xml键值对通过这个方法加载到该对象中
         */
        bool Import(Dictionary<string, object> values);
        /*
         * 对象通过这个方法导出成Xml节点
         */
        bool Export(XmlNode parent);
    }

    public class XmlUtility
    {
        public static XmlElement LoadXmlString(string xmlString)
        {
            XmlDocument doc = new XmlDocument();
            if (!XmlUtility.LoadXmlDoc(doc, xmlString))
            {
                return null;
            }
            return doc.DocumentElement;
        }
        public static XmlElement LoadXmlFile(FileInfo xmlFile)
        {
            if (!xmlFile.Exists)
            {
                return null;
            }
            XmlDocument doc = new XmlDocument();
            if (!XmlUtility.LoadXmlDoc(doc, xmlFile))
            {
                return null;
            }
            return doc.DocumentElement;
        }
        public static XmlElement LoadXmlPath(string xmlPath)
        {
            return XmlUtility.LoadXmlFile(new FileInfo(xmlPath));
        }
        private static bool LoadXmlDoc(XmlDocument doc, string xmlString)
        {
            try
            {
                doc.LoadXml(xmlString);
                return true;
            }
            catch (XmlException e)
            {
                Console.Error.WriteLine(e);
                return false;
            }
        }
        private static bool LoadXmlDoc(XmlDocument doc, FileInfo xmlFile)
        {
            try
            {
                doc.Load(xmlFile.FullName);
                return true;
            }
            catch (Exception e)
            {
                Console.Error.WriteLine(e);
                return false;
            }
        }

        public static XmlElement CreateElement(XmlDocument doc, string name)
        {
            if (doc == null)
            {
                throw new Exception("静态函数:CreateElement 参数:doc 空指针");
            }
            if (name == null)
            {
                throw new Exception("静态函数:CreateElement 参数:name 空指针");
            }
            if (string.IsNullOrWhiteSpace(name))
            {
                return null;
            }
            return doc.CreateElement(name);
        }
        public static XmlElement CreateTextElement(XmlDocument doc, string name, string value)
        {
            if (doc == null)
            {
                throw new Exception("静态函数:CreateTextElement 参数:doc 空指针");
            }
            if (name == null)
            {
                throw new Exception("静态函数:CreateTextElement 参数:name 空指针");
            }
            value = value == null ? "" : value;
            XmlElement element = CreateElement(doc, name);
            if (element == null)
            {
                return null;
            }
            element.InnerText = value;
            return element;
        }
        public static XmlElement CreateCDataTextElement(XmlDocument doc, string name, string value)
        {
            if (doc == null)
            {
                throw new Exception("静态函数:CreateTextElement 参数:doc 空指针");
            }
            if (name == null)
            {
                throw new Exception("静态函数:CreateTextElement 参数:name 空指针");
            }
            value = value == null ? "" : value;
            XmlElement element = CreateElement(doc, name);
            if (element == null)
            {
                return null;
            }
            element.AppendChild(element.OwnerDocument.CreateCDataSection(value));
            return element;
        }
        public static XmlElement AppendElement(XmlNode parent, string name)
        {
            XmlElement child = CreateElement(parent.OwnerDocument, name);
            parent.AppendChild(child);
            return child;
        }
        public static XmlElement AppendTextElement(XmlNode parent, string name, string value)
        {
            XmlElement child = CreateTextElement(parent.OwnerDocument, name, value);
            parent.AppendChild(child);
            return child;
        }
        public static XmlElement AppendAttribute(XmlElement current, string name, string value)
        {
            XmlAttribute attribute = current.OwnerDocument.CreateAttribute(name);
            attribute.Value = value;
            current.Attributes.Append(attribute);
            return current;
        }

        public static bool ReplaceChildTextElement(XmlNode parent, string name, string value)
        {
            XmlNode child = parent.SelectSingleNode("./" + name);
            if (child == null)
            {
                return false;
            }
            child.InnerText = value;
            return true;
        }

        public static XmlElement AppendCDataTextElement(XmlNode parent, string name, string value)
        {
            if (parent == null)
            {
                throw new Exception("静态函数:AppendCDataTextElement 参数:parent 空指针");
            }
            if (name == null)
            {
                throw new Exception("静态函数:AppendCDataTextElement 参数:name 空指针");
            }
            XmlElement child = CreateCDataTextElement(parent.OwnerDocument, name, value);
            parent.AppendChild(child);
            return child;
        }
        public static XmlElement AppendTextElements(XmlNode parent, string keyName, string valueName, List<string> values)
        {
            if (parent == null)
            {
                throw new Exception("静态函数:AppendTextElements 参数:parent 空指针");
            }
            if (keyName == null)
            {
                throw new Exception("静态函数:AppendTextElements 参数:keyName 空指针");
            }
            if (valueName == null)
            {
                throw new Exception("静态函数:AppendTextElements 参数:valueName 空指针");
            }
            XmlElement child = AppendElement(parent, keyName);
            foreach (string value in values)
            {
                AppendTextElement(child, valueName, value);
            }
            return child;
        }
        public static XmlElement AppendCDataTextElements(XmlNode parent, string keyName, string valueName, List<string> values)
        {
            if (parent == null)
            {
                throw new Exception("静态函数:AppendCDataTextElements 参数:parent 空指针");
            }
            if (keyName == null)
            {
                throw new Exception("静态函数:AppendCDataTextElements 参数:keyName 空指针");
            }
            if (valueName == null)
            {
                throw new Exception("静态函数:AppendCDataTextElements 参数:valueName 空指针");
            }
            XmlElement child = AppendElement(parent, keyName);
            foreach (string value in values)
            {
                AppendCDataTextElement(child, valueName, value);
            }
            return child;
        }
        public static string ReadChildTextElement(XmlNode parent, string name, string defaultValue = "")
        {
            XmlNode child = parent.SelectSingleNode("./" + name);
            if (child == null)
            {
                return defaultValue;
            }
            return ReadTextElement(child, defaultValue);
        }

        public static string ReadTextElement(XmlNode current, string defaultValue = "")
        {
            if (current == null)
            {
                throw new Exception("静态函数:ReadTextElement 参数:current 空指针");
            }
            if (current.ChildNodes.Count != 1)
            {
                return defaultValue;
            }
            XmlNode child = current.FirstChild;
            if (child.NodeType == XmlNodeType.CDATA)
            {
                return (child as XmlCharacterData).InnerText;

            }
            else if (child.NodeType == XmlNodeType.Text)
            {
                return child.InnerText;
            }
            return defaultValue;
        }
        public static List<string> ReadTextElements(XmlNode current, string valueName, string defauleValue = "")
        {
            if (current == null)
            {
                throw new Exception("静态函数:ReadTextElements 参数:current 空指针");
            }
            List<string> values = new List<string>();
            XmlNodeList children = current.SelectNodes(valueName);
            if (children == null)
            {
                return values;
            }
            foreach (XmlNode child in children)
            {
                string value = ReadTextElement(child, defauleValue);
                if (value != null)
                {
                    values.Add(value);
                }
            }
            return values;
        }

        public static void WriteXmlFile(string file, string rootName, XmlSerializable data)
        {
            XmlDocument doc = new XmlDocument();
            XmlElement root = doc.CreateElement(rootName);
            if (data != null)
            {
                data.Export(root);
            }
            WriteXmlFile(file, root);
        }
        public static bool WriteXmlFile(string file, XmlNode root)
        {
            FileInfo fileInfo = new FileInfo(file);
            FileUtil.EnsureDir(fileInfo.Directory);
            try
            {
                using (XmlWriter writer = XmlWriter.Create(file, DefaultXmlWriterSettings()))
                {
                    root.WriteTo(writer);
                    writer.Flush();
                }
                return true;
            }
            catch (Exception e)
            {
                Console.Error.WriteLine("{0}", e.ToString());
            }
            return false;

        }
        private static XmlWriterSettings DefaultXmlWriterSettings()
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Encoding = Encoding.UTF8;
            settings.Indent = true;
            settings.IndentChars = "  ";
            settings.NewLineChars = "\n";
            return settings;
        }
    }
}
